﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NetCore.Ado;
using NetCore.Cache;
using NetCore.EF;
using Autofac;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.FileProviders;
using NetCore.BLL;
using Microsoft.Extensions.Caching.Distributed;
using log4net;
using log4net.Config;

namespace NetCore.Web
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }
        public Autofac.IContainer ApplicationContainer;

        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
         
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => false;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
            //负载均衡中间件
            services.Configure<ForwardedHeadersOptions>(options =>
            {
                options.ForwardedHeaders =
                    ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
            });
            ///log4日志记录
            LogHelper.Repository = LogManager.CreateRepository("NETCORERepository");
            XmlConfigurator.Configure(LogHelper.Repository, new FileInfo("log4net.config"));
            services.AddResponseCompression();//压缩文件
            //系统配置
            ApplicationEnvironments.Site = Configuration.GetSection("SiteConfig").Get<SiteConfig>();
            ApplicationEnvironments.ProviderType = (ProviderType)Enum.Parse(typeof(ProviderType), ApplicationEnvironments.Site.ProviderType);
            //httpcontext 如果在dll层需要用到httpContext则需要使用方法
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
           
            //属性注入
            services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
            services.AddMvc(config => {
                config.Filters.Add<CustomExceptionFilter>();
                config.ModelValidatorProviders.Clear();
                config.CacheProfiles.Add("default", new Microsoft.AspNetCore.Mvc.CacheProfile
                {
                    Duration = 600 /*10分钟*/
                });
                // config.ModelValidatorProviders.Insert(0, new CustomModelValidatorProvider());
                //  config.ModelBinderProviders.Insert(0, new CustomModelBinderProvider());

            }
            )
            //全局配置Json序列化处理
        .AddJsonOptions(options =>
        {
            //忽略循环引用
            options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            //不使用驼峰样式的key
            options.SerializerSettings.ContractResolver = new DefaultContractResolver();
            ////设置时间格式
            //options.SerializerSettings.DateFormatString = "yyyy-MM-dd";
        }
        )
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
                .AddSessionStateTempDataProvider();
            #region  //身份认证时需要使用的方法
            ICacheService cacheService = null;
            //缓存注入
            if (ApplicationEnvironments.Site.IsUseRedis)
            {
                cacheService = new RedisService();
            }
            else
            {
                cacheService = new MemoryService();
            }
            services.AddSingleton<ICacheService>(cacheService);
            services.AddSingleton<IDistributedCache>(cacheService);
            if (ApplicationEnvironments.Site.IsUseRedis)
            {
                //services.AddDistributedRedisCache(options =>
                //{
                //    options.Configuration = ApplicationEnvironments.Site.Redis.Connection;
                //    options.InstanceName = ApplicationEnvironments.Site.Redis.InstanceName;
                //});
                 
                //services.AddDataProtection()
                //      .SetApplicationName(ApplicationEnvironments.Site.Redis.InstanceName)
                //      //.UseCryptographicAlgorithms(
                //      //new AuthenticatedEncryptorConfiguration()
                //      //{
                //      //    EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
                //      //    ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
                //      //})
                //      .PersistKeysToStackExchangeRedis(RedisService.GetMultiplexer(), "DataProtection-Keys");
              
            }
            else {
              //  services.AddDistributedMemoryCache();
               // services.AddDataProtection().SetApplicationName(ApplicationEnvironments.Site.Redis.InstanceName)  ;

            }
            services.AddSession(options =>
            {
                options.Cookie.HttpOnly = true;
               // options.Cookie.Domain = ApplicationEnvironments.Site.Domain;
                options.Cookie.Path = "/";
                options.Cookie.Name = ApplicationEnvironments.Site.CookieName;
                options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
                options.IdleTimeout = TimeSpan.FromMinutes(ApplicationEnvironments.Site.SessionTimeout);
            });
            services.AddDirectoryBrowser();
            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
            {
               // options.Cookie.Domain = ApplicationEnvironments.Site.Domain;
                 options.DataProtectionProvider= DataProtectionProvider.Create(new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory+ "/shared-auth-ticket-keys/"));
                options.Cookie.Name = ApplicationEnvironments.Site.CookieName;
                options.Cookie.Path = "/";
                options.LoginPath = new PathString("/login");
                options.AccessDeniedPath = new PathString("/Forbidden"); //禁止访问路径：当用户试图访问资源时，但未通过该资源的任何授权策略，请求将被重定向到这个相对路径。
               // options.SlidingExpiration = false;  //Cookie可以分为永久性的和临时性的。 临时性的是指只在当前浏览器进程里有效，浏览器一旦关闭就失效（被浏览器删除）。 永久性的是指Cookie指定了一个过期时间，在这个时间到达之前，此cookie一直有效（浏览器一直记录着此cookie的存在）。 slidingExpriation的作用是，指示浏览器把cookie作为永久性cookie存储，但是会自动更改过期时间，以使用户不会在登录后并一直活动，但是一段时间后却自动注销。也就是说，你10点登录了，服务器端设置的TimeOut为30分钟，如果slidingExpriation为false,那么10:30以后，你就必须重新登录。如果为true的话，你10:16分时打开了一个新页面，服务器就会通知浏览器，把过期时间修改为10:46。 更详细的说明还是参考MSDN的文档。
            });
            #endregion
            ApplicationEnvironments.DefaultSession = new BaseController();
            //数据库驱动注入
            if (ApplicationEnvironments.Site.IsUseEF)
            {
                services.AddScoped<IDbRepository, EFRepository>();
            }
            else
            {
                services.AddScoped<IDbRepository, AdoRepository>();
            }
           
               //service 层注入
            var files = System.IO.Directory.GetFiles(AppContext.BaseDirectory, "*.BLL.dll");
            if (files != null && files.Length > 0)
            {
                foreach (var file in files)
                {
                    foreach (var item in GetClassName(file))
                    {
                        foreach (var typeArray in item.Value)
                        {
                            services.AddScoped(typeArray, item.Key);
                        }
                    }
                }
               
            }


            #region AutoFac
            var builder = new Autofac.ContainerBuilder();
            builder.Populate(services);
            #region  mvc 区域分项目时调用
            var manager = new ApplicationPartManager();
            files = System.IO.Directory.GetFiles(AppContext.BaseDirectory, "*.Web.dll");//获取所有的web.dll
            if (files != null && files.Length > 0)
            {
                foreach (var file in files)
                {
                    manager.ApplicationParts.Add(new AssemblyPart(Assembly.LoadFrom(file)));
                }
            }
            manager.FeatureProviders.Add(new ControllerFeatureProvider());
            var feature = new ControllerFeature();
            manager.PopulateFeature(feature);
            #endregion

            //采用属性注入控制器
            builder.RegisterTypes(feature.Controllers.Select(ti => ti.AsType()).ToArray()).PropertiesAutowired();
            this.ApplicationContainer = builder.Build();

            
            return new AutofacServiceProvider(this.ApplicationContainer);
           
            #endregion
            //跨域访问

            //services.AddCors(options => options.AddPolicy("AllowSameDomain", builder => builder.WithOrigins("http://a.local.com:50307", "http://b.local.com:63455")));
            //跨域认证时使用此项
            //services.AddCors(options => options.AddPolicy("AllowSameDomain", builder => builder.WithOrigins("http://a.local.com:50307", "http://b.local.com:63455").AllowCredentials()));
        }
        private static Dictionary<Type, Type[]> GetClassName(string assemblyName)
        {
            if (!String.IsNullOrEmpty(assemblyName))
            {
                Assembly assembly = Assembly.LoadFrom(assemblyName);
                List<Type> ts = assembly.GetTypes().ToList();

                var result = new Dictionary<Type, Type[]>();
                foreach (var item in ts.Where(s => !s.IsInterface))
                {
                    var interfaceType = item.GetInterfaces();
                    if (item.IsGenericType) continue;
                    result.Add(item, interfaceType);
                }
                return result;
            }
            return new Dictionary<Type, Type[]>();
        }
        
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            ///负载均衡中间件
            app.UseForwardedHeaders();
            app.UseExceptionHandler("/Home/Error");//错误处理
            if (env.IsDevelopment())
            {
                //app.UseBrowserLink();
                //app.UseDeveloperExceptionPage();
                ApplicationEnvironments.IsDevelopment = true;
            }
            else
            {
                //app.UseExceptionHandler("/Home/Error");
                //app.UseExceptionHandler("/Home/Error");
                //app.UseHsts();
                ApplicationEnvironments.IsDevelopment = false;
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles(new StaticFileOptions()
            {
                FileProvider = new PhysicalFileProvider(
                Path.Combine(ApplicationEnvironments.DefaultDirectory, @"wwwroot")
                ),
                OnPrepareResponse = ctx =>
                {
                    ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=6000");
                }
            });
            app.UseStaticFiles(new StaticFileOptions()
            {
                FileProvider = new PhysicalFileProvider(
                Path.Combine(ApplicationEnvironments.BaseDirectory, @"upload")
                ),
                RequestPath = new PathString("/static/cloud"),
                OnPrepareResponse = ctx =>
                {
                    ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=600");
                }

            });
            app.UseDirectoryBrowser(new DirectoryBrowserOptions()
            {
                FileProvider = new PhysicalFileProvider(
                     Path.Combine(ApplicationEnvironments.BaseDirectory, @"upload")),
                RequestPath = new PathString("/static/cloud"),
                
            });
        //app.UseSpaStaticFiles();
        app.UseCookiePolicy();
            app.UseSession();
            app.UseAuthentication();

            app.UseResponseCompression();//文件压缩
            app.Map("/socket/connect", WebSocketHelper.Map);
            //app.UseErrorHandling();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                name: "areaRoute",
                template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");//控制器分层
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}"
                    );
            });
            
            //添加httpcontext类
            AppHttpContext.Configure(app.ApplicationServices.
     GetRequiredService<Microsoft.AspNetCore.Http.IHttpContextAccessor>());
            
           // ApplicationEnvironments.DefaultSession= 
            //跨域访问
            // app.UseCors("AllowSameDomain");
            //app.UseSpa(spa =>
            //{
            //    // To learn more about options for serving an Angular SPA from ASP.NET Core,
            //    // see https://go.microsoft.com/fwlink/?linkid=864501

            //    spa.Options.SourcePath = "ClientApp";

            //    if (env.IsDevelopment())
            //    {
            //        spa.UseAngularCliServer(npmScript: "start");
            //    }
            //});
            //nginx反向代理
            app.UseForwardedHeaders(new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            });

           
            InitDataService initData = new InitDataService();
            ApplicationEnvironments.DefaultSession = new ApplicationSession();
            ApplicationEnvironments.Site.IsWeb = false;
            EFRepository eFRepository = new EFRepository();
            eFRepository.DbEnsureCreated();
            eFRepository.Dispose();
            ApplicationEnvironments.Site.IsWeb = true;
            ApplicationEnvironments.DefaultSession = new BaseController();
            app.UseAuthentication();
            //app.UseStatusCodePages();//使用HTTP错误代码页
        }

    }
    public class ApplicationSession : IApplicationSession
    {
        public string UserId => "1000000000";

        public string UserName => "";
        public string UserToken => "";
        public string IPAddress => "";

        public string FormAddress => "";

        public string ServerAddress => "";

        public void SetToken(string value)
        {

        }

        public T GetUser<T>() where T : class, new()
        {
            throw new NotImplementedException();
        }
    }
}
